package com.star.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.security.Key;
import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Java web token 工具类
 *
 * @author JXC
 * @date 2021/4/26
 */
public class JavaWebToken {
    /**
     * 过期时间一天，
     * TODO 一天
     */
    private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000;
    /**
     * token私钥
     * 一旦得到该密钥也就可以伪造token了
     */
    private static final String TOKEN_SECRET = "StarOrangeabc123";

    /**
     * JWT由3个部分组成,分别是 头部Header,载荷Payload一般是用户信息和声明,签证Signature一般是密钥和签名
     * 当头部用base64进行编码后一般都会呈现eyJ...形式,而载荷为非强制使用,签证则包含了哈希算法加密后的数据,包括转码后的header,payload和sercetKey
     * 而payload又包含几个部分,issuer签发者,subject面向用户,iat签发时间,exp过期时间,aud接收方。
     *
     * @throws
     * @Title: generToken
     * @Description: TODO
     * @param: @param id 用户id
     * @param: @param issuer 签发者
     * @param: @param subject 一般用户名
     * @param: @return
     * @return: String
     */
    public static String generToken(String id, String issuer, String subject) {
        long ttlMillis = EXPIRE_TIME;
        // 使用Hash256算法进行加密
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        // 获取当前时间戳
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        // 将秘钥转成base64形式再转成字节码
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(TOKEN_SECRET);
        // 对其使用Hash256进行加密
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        // JWT生成类，此时设置iat， 以及根据传入的id设置token
        JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now);
        // 由于payload（有效载荷）是非必须的，所以这时加入检测
        if (subject != null) {
            builder.setSubject(subject);
        }
        if (issuer != null) {
            builder.setIssuer(issuer);
        }
        //由于Payload是非必须加入的,所以这时候要加入检测
        builder.signWith(signatureAlgorithm, signingKey);
        //进行签名,生成Signature
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        //返回最终的token结果
        return builder.compact();
    }

    /**
     * 此函数用于更新token
     *
     * @param token token
     * @return String
     */
    public static String updateToken(String token) {
        // Claims 就是包含了我们的payload信息类
        Claims claims = verifyToken(token);
        String id = claims.getId();
        String subject = claims.getSubject();
        String issuer = claims.getIssuer();
        // 生成新的token，根据现在的时间
        return generToken(id, issuer, subject);
    }

    /**
     * 将token解析，将payload信息包装成Claims类再返回
     *
     * @param token token
     * @return Claims
     */
    private static Claims verifyToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(TOKEN_SECRET))
                .parseClaimsJws(token).getBody();
        return claims;
    }
}

